ScalaMatsuri 2016 一日目 最速レポート【パートB】
はじめに
ScalaMatsuri 2016 一日目の方に荒井と一緒に参加してきました。本記事はそのまとめBパートになります。Aパートの記事と合わせてご覧ください。
Refactoring in Scala (中村 学 (がくぞ)氏 @gakuzzzz)
http://gakuzzzz.github.io/slides/refactoring_in_scala
自己紹介
- tech to value
- professional null cleaner
value typeについて
- dddでいう値オブジェクト
リポジトリとサービスを例に
- サンプルコードには問題がある
- マップの意味がはっきりしない
- ユーザーが見つからない時にNoSuchElementExceptionが出る
解決策1 タイプエイリアスを使う
- マップの意味がはっきりする
- しかしuserMapの引数がコンパイラチェック通らない
解決策2 Tagged typeを使う
- Scalaz shapeless で使われている
- 中置記法でわかりやすくなる
- 間違ったIDを指定するとコンパイラエラーになる
- 内部ではunwrap wrapせずにcastになるからコストがかからない
Value class を使う
- ひとつのコンストラクタ引数を持つクラスを作り、AnyValを継承させる
- コンパイラが最適化してくれる
- メモリ割り当てが意図せずされる場合があるから、使用には注意する(配列、パターンマッチ)
- Scala標準ドキュメントにProsConsが詳しく書かれている
- IDとかいちいち宣言するのめんどくさい
ファントムタイプ
- 型引数付きのID型をつくる
- コンパイルすると幽霊(phantom) のように消えてしまう
ここまでまとめ
- 意味と物理的データ型を区別する
- 型チェックを効果的に使う
- ジェネリクスを使う
レイヤーの境界での型変換
- 外部レイヤはプロダクトを知らない
- 型変換を効果的に使う
- PlayのFormatter, QueryStringBindable
- Slick3のColumnType
- しかし、書くの億劫 IDとかNameを変換するだけでも
Iso, Prism
- Isoは変換できることを示す。2つの型が相互変換可能なことを表す。
- PrismはIsoに似ている。片方の変換が失敗するかもしれないような変換(Optionを使う)
- IsoはPrismの継承型にできる
- Monocle, shaplessに定義されてる(自分で定義してもOK)
- もちろんScalazにもある
- レイヤに依存しないのでDBに特化した形にはならないので安心して定義できる
- 各レイヤ間での変換を共通化できる
implicit macro
- Isoの定義はボイラープレートなのでimplicit macroを使える(型情報をもとに自動で変換してくれる)
- Scala公式DocのMacroの例にIsoが取り上げられている
- Experimentalなので将来的にはなくなる
ここまでまとめ
- Iso, Prismを使うとレイヤに依存しない変換コードをかける
- DRYにもできる
Scalac の深みへダイブ (Chris Birchall氏 @cbirchall)
Who am i
- エムスリー卒
- macroおじさん
なぜ面白いか
- マクロを書く時に役立つ
- コンパイラが遅い時役立つ
構文木(コード例)
- AST
- リフレクションをインポート
- StringをScalaソースコードとして構文解析してくれる
型
- Phantomとかいろいろな種類の型がある
- なんとなくみんなわかってると思う
Symbol
- レファレンスできるものについては必ず名前がついてる
- シンボルテーブルに格納されている
Compilation
- 構文木→シンボルテーブル→型付構文木→構文木変換をへる
- シンボルテーブルから型付構文木への変換は非常に難しい処理がなされる
- ヒューマンリーダブルなJavaByteコードに近づいていく
- 最終的に中間表現に(今はJVMだけ)
- JVMだけだから意味がないのでTypeTreeから直接変換されるようになっている(2.12から)
コンパイラはなぜこんなに時間がかかるのか
- typerが結局遅い
- patmat phaseはtyperの後
- patmatの後にはJavaByteコードに更に近づく
Scalacの歴史
- NSC
- .Netバックエンドへのサポート
- 2.12からバックエンドオプティマイザが導入された
ケーキパターン
- コンパイラがもともと使っていた
- traitの自分型アノテーションを用いて宣言している
- 多くの場合依存関係がある
コンパイラのケーキパターン
- globalのtraitは大変多い
- シンボルテーブルが多くのtraitに依存している
- CompilationUnit = 1ソース・ファイルに相当
- 変換を手伝うユーティリティークラスがいろいろある
プラグイン
- テストカバレージ、ScalaJS、マクロ、SBTのインクリメンタルコンパイル
- どのフェーズの後で実行するか指定できる
- Scalaをベーシックに変換するプラグインを書いてみた
デモ(プラグイン)
- Scalacのフラグにプラグインを指定する
- すると新しいフェーズが追加される
- 余り役に立つプラグインではないですが、Githubに公開中
デモ(JsonASTのプラグイン)
- jsonリテラルをScalaの中に作った
- Githubにこちらも公開中
まとめ
- 参考文献
- Dotty
- エンジニアリングの素晴らしさ。コンパイラは素晴らしい、機能してくれるだけで感謝しよう
質疑応答
- 新しいコンパイラはよりImmutableでFunctionalになるはずだ
- Dottyはパフォーマンスが下がるかわからない
楽しく役立つ Scala リファクタリング (Tomer Gabel氏 @tomerg)
[slideshare id=57668866&doc=scalamatsuri2016-scalarefactoringforfunandprofitjapanesesubtitles-160130015814]
アジェンダ
- パターンとアンチパターンを議論します
- ScalaChessを例にする
Stringly Typed(Bad practice)
1. パースしてないデータ
- パースはアクセスの度に行うべきでない
- ボイラープレートが多くなってしまう
- パース後のデータを使うことで効率的でエラー処理が簡潔になる
2. Optionの代わりに空文字を使う
- フィールドの一部が空であった時のチェックが微妙に
デモ
- ScalaChessライブラリ
- Chess domain ライブラリ
- Stringを空白でわけてコマの動きを表現している
- これはMoveのListとして本来は表現されるべき
- Openingを外部から使う場合まずパースするメソッドを用意する
Collective Abuse(Bad practice)
- コレクションフレームワークを乱用するとコードが読めなくなる
- too many inline steps(一行に処理を詰め込み過ぎる→中間状態に名前をつけよう!
- タプル使いすぎ問題→ケースクラスを使おう
デモ
- case classの内部に意味のあるローカル関数をつくってリファクタリングできる
Dockerをベースとしたインフラ上でのPlay frameworkアプリケーション本番運用ノウハウ (Naoki Ainoya氏 @naokiainoya)
自己紹介
- リクルートマーケティングパートナーズ
- 英単語アプリ
Backend
- DDDを用いるためにScalaつかった
DDD
- Scalaz使っている
Docker
- デプロイも簡単にできる
- Scalaのアプリケーションのデプロイについて
deploy flow
- Github flow を使っている
- JenkinにDocker publish任せる
- ECSがデプロイする対象を選ぶ(サービスが拡張してもOK)
- EB使わなかったのは一年前始まったから(EBはメンテコストが高い)
- confが各アプリに用意されている
Database management
- evolutionのmigrationを使っている
- useLockのオプションが有効でも注意して行っている
ログ
- Errbit使っている
アジアから Scala OSS に貢献するということ (瀬良 和弘氏 @seratch)
[slideshare id=57690395&doc=scalamatsuri2016-160131002523]
- 満員御礼のため、会場に入れず、スライドのみになります。申し訳ございません。
みんなの関数型プログラミング (Zach McCoy氏 @ZachAMcCoy)
[slideshare id=57672401&doc=fpforall-160130065225]
自己紹介
- Scala歴四年くらい
関数型プログラミングとは
- 関数を第1級値として扱う
- 副作用をコントロールする
- 中核は純粋関数で外側の層で副作用が起こる
関数とはなにかを数学的に理解する
- 関数の中にドメインとコドメインに変換する
- 結果が一意
純粋関数
- hash
- 演算
- スコープの中だけを参考にする
- 再利用性
- 演算と入力を分離
参照透過性
- 式を値と置き換えることが可能
- 数学的に考えると 2 * 2 は 4
- 等式推論できるということはリーズニングがうまくできるということ
- ScalaはIOが関連する可能性がある
質疑応答
OOとFPを共存させる方法は?
- 純粋関数はどこでも書くことができる
- どちらかに制約があるわけではない
あなたのScalaを爆速にする7つの方法 (井上 ゆり氏 @iyunoriue)
[slideshare id=57670004&doc=scalaja-160130032955]
前提
- 並列環境は考慮していない
- ベンチ環境(EC2 memory 30G 8 core)
- QA形式で
2Gのファイル、SSDとmemcache(チャンク付き)から呼び出すのどちらが早い?
- 答え: SSD
-
memcacheの分割には時間がかかる
- ByteBufferについてsparkの責任者の人がGistにまとめてる
for内包表記とflatMap, mapを使うのとどちらが早いか
-
答え: 同じ
-
実験の結果、同じだった
- JavaByteデコンパイルした結果、全く同じだった
var Vector val ArrayBufferのappendどちらが早い?
-
答え: ArrayBuffer
-
VectorとArrayBufferで更新の方法が異なる
var List val ListBuffer どちらが先頭挿入が早い?
-
答え: List
-
Listはhead, tailを別々に管理しているため
- ListBufferは内部変数を持っているため更新が遅いようだった
var List val ListBuffer どちらが削除が早いか(先頭削除、末尾削除どちらも)
-
答え: ListBuffer
-
Buffer系は削除に優れている
- StreamはStackoverflowを引き起こした
Vector ListBuffer randomreadが早いのはどっち?
-
答え: Vector
-
Vector, Array ArrayBufferがrandomReadに優れている
Stream Array どちらがFibonacchi数列をつくれる?
-
答え: Stream
-
遅延評価のため
- StreamをListに具現化するのは遅い
- (オーディエンスによるとIntegerのunboxingが時間計算量の大きさの支配的な要因なのではとのこと)
\w+の正規表現のマッチ findAllInと後方後よみ findPrefixOfと量指定子付きどちらが早い?
-
答え: findPefixOfと量指定子付き
-
前者は/.+でbacktrackingが発生する
- findPrefixOfは早い
- 正規表現を使うときはBacktrackingを発生させないようにしよう
関数型、代数的なドメイン・モデリングの方法 (Debasish Ghosh氏 @debasishg)
[slideshare id=57755268&doc=algebradomainsubtitles-160201214752]
Domain modeling
- Domain modelingは問題領域に関連している
- Banking、口座、取引
- 関数、代数から考える
サーバーを関数として考える
- Twitter社でのサーバーの機能構成はFPに基づく
- ドメインモデルは関数の集合体である
境界づけられたコンテキスト
- 複数のBoundedContextの中で使われるModelがドメインモデル
- domain functionはドメイン関数で他の関数と合成できる
- business roleに従う
ドメインモデル代数
- 型制約でドメインを表す
- エンティティは代数的データ型
投資銀行の具体的な例
- クライアント、マーケット、トレードをドメイン代数にして取引をtraitとして一塊の関数を用いて表す
- 引数に渡すのはBoundedContext(?)
抽象的な関数型インターフェイス
- リストをMonadに置き換える
- 作用付き関数合成をKleisliとして表す
- モナド付きの関数をKleisli arrowで表す
- 取引の一連の流れを代数(Kleisli arrowも代数と見立てる)の合成のみで表せる
エラー処理
- KleisliでのListは型コンストラクタ
- ListをM[List[_]]としてエラー処理のためのモナドM(モナドトランスフォーマー)で囲む
- モナドトランスフォーマーはいくつかのモナドを合成する(cf: OptionT)
- M[List[_]]を全体として扱うためのモナドListTを用いる
- StringOr[A] = String \/ A , Valid[A] = ListT[StringOr, A]
型代数
- disjunction型
- 様々な関数型コンビネータを提供する
- エッジケースは個別の処理を書く
- 代数法則としてドメイン不変条件を表現する
- プロパティベースのテストとして代数法則を用いる
最後に
- 本書いてます
質疑応答
- BoundedContextの識別の仕方をどうすればいいか→DDD本に書いてあります。
Scalaでドメイン駆動設計に真正面から取り組んだ話 (藤井 善隆氏 @yoshiyoshifujii)
[slideshare id=57679140&doc=scalamatsuri2016en-160130141511]
はじめに
- 会社紹介
DDD
- 設計手法
- 実装しようと思った時に迷うと思う
- IDDD(実践ドメイン駆動)読んでも難しい
Layerd archtecture
- レイヤー化アーキテクチャ
- レイヤーの責務を細分化する
- ユーザーに情報を表示する(UIレイヤー)
- アプリケーションレイヤ
- ドメインレイヤ
- インフラレイヤ
- どうやって実装しようか最初に考えた
- アプリケーションレイヤにViewModelをおいた
- MVVMを参考にViewModelは作った
- エンティティはインフラに依存していた
- UIレイヤにドメインのコードが混じり始めた
- アプリケーション層にドメインのコードが交じる
- プログラム的に解決するよりもコミュニケーションで解決した
- 分析モデリングを開発のイベントとして用意した
- シーケンス図を書いて確認した
- 曲者のサービス
- サービスがFatになってしまう(Application, Domain問わず)
- サービスを多用するとドメインロジックが流出する
- 一旦サービスとして置いてリファクタリングで解決していこう
- リファクタリングする日をイテレーションの間に設けた
- コンバータ書くのだるい
- CQRSを採用(DDD)
CQRS
- Command, Query, Responsibility, Separate
- Queryモデルを作成した
- Commandは使わなかった
is a Root Entity
- Root Entityが本当に適しているか?
- Root Entityのサブ型がレポジトリを必要とするか?
Message
- 例外処理のメッセージをどうするか
- インフラ側のエラーはログに残してUIにはシステムエラーとして出す
- Noneを選ぶかTryのFailureを選ぶか悩む
- EitherはScalazの方がMonadicだからいい
- Validation(Scalaz)もいい
- Applicativeまでしか満たさないからMonadicにはできない
- Applicativeは癖がある
- StackするならValidation使う、アプリケーションのロジックふえたらいろいろ考えたい
まとめ
- 悪戦苦闘してます
- 悩みを解決することに少しでも役に立てたなら幸いです。
質疑応答
- サブ型をつくるならis a関係(テーブルが同じ)じゃなきゃおかしいのでは。リポジトリはその場合ひとつにできるのでは(かとじゅんさん)
おわりに
回ったセッションはドメイン駆動と関数型に特化したものが多い印象でした!
難しい話もありましたが、今後の開発に何らかの形で活かせればとおもっています!